import { useDebounceFn, tryOnUnmounted } from '@vueuse/core'
import { ref, watch } from 'vue'

import * as d3 from 'd3'

// 分数段
const score_level = [100, 85, 70, 60, 1, 0]
// 层级颜色
const color_level = [
  '#1a8440',
  '#37c276',
  '#b3d465',
  '#ff7347',
  '#ee2628',
  '#cecdcd'
]
// 获取节点颜色
const getNodeColor = data => {
  let { class_rate } = data
  let num = Number(class_rate).toFixed(3) * 100
  let index = score_level.findIndex(item => item <= num)
  return color_level[index]
}
// 获取节点大小
const getNodeSize = (depth, type) => {
  let size = depth === 0 ? 18 : 10 - 2 * depth
  size = size > 5 ? size : 5
  return type === 'hover' ? size + 6 : size
}

export const useD3Graph = (chartsData, Dom, options) => {
  const prevNode = ref(null)
  const NODES = ref(null)
  const LINKS = ref(null)
  const TOOLTIP = ref(null)

  const createChart = () => {
    console.log(Dom.value)

    const dom = d3.select(Dom.value) || d3.select('#target-d3-graph')

    if (!dom) return console.error('dom is null')
    if (!chartsData.value) return
    dom.selectAll('*').remove()

    const width = dom.node().getBoundingClientRect().width
    const height = dom.node().getBoundingClientRect().height
    const radius = Math.min(width, height) / 2 - 80
    const cx = width * 0.5
    const cy = height * 0.48

    const tree = d3
      .tree()
      .size([2 * Math.PI, radius])
      .separation((a, b) => (a.parent === b.parent ? 1 : 4) / a.depth)

    const root = d3
      .hierarchy(chartsData.value)
      .sort((a, b) => d3.ascending(a.data.id, b.data.id))
    tree(root)

    // 树的最大高度
    const maxDepth = root.height

    const rand = (() => {
      const backup = {}
      let leafRadiusAdjustment = 5
      let cnt = 0
      return id => {
        if (id && backup[id] !== undefined) {
          return backup[id]
        }
        cnt = (cnt + 1) % 4
        const ret = leafRadiusAdjustment
        leafRadiusAdjustment = 15 + 15 * cnt
        if (id) {
          backup[id] = ret
        }
        return ret
      }
    })()

    const getRadius = d => {
      return d.children && d.depth < maxDepth - 1 ? d.y : d.y + rand(d.data.id)
    }

    const svg = dom
      .append('svg')
      .attr('id', 'd3-graph-svg')
      .attr('width', width)
      .attr('height', height)
      .attr('viewBox', [-cx, -cy, width, height])
      .attr('style', 'width: 100%; height: auto; font: 10px sans-serif;')
      .call(
        d3
          .zoom()
          .scaleExtent([0.5, 5]) // 设置缩放的最小值为0.5，最大值为5
          .on('zoom', event => {
            svg.attr('transform', event.transform)
          })
      )
      .attr('transform', `scale(0.5)`)
      .append('g') // 添加一个 g 元素来包含所有图表内容

    const links = svg
      .append('g')
      .attr('fill', 'none')
      .attr('stroke', '#999')
      .attr('stroke-opacity', 0.4)
      .attr('stroke-width', 1.2)
      .selectAll('path')
      .data(root.links())
      .join('path')
      .attr(
        'd',
        d3
          .linkRadial()
          .angle(d => d.x)
          .radius(d => getRadius(d))
      )
      .attr('id', d => `link-${d.source.data.id}-${d.target.data.id}`)

    const nodes = svg
      .append('g')
      .selectAll('circle')
      .data(root.descendants())
      .join('circle')
      .attr('id', d => `node-${d.data.id}`)
      .attr('cursor', 'pointer')
      .attr(
        'class',
        d => 'node' + (d.children ? ' node--internal' : ' node--leaf')
      )
      .attr(
        'transform',
        d =>
          `rotate(${(d.x * 180) / Math.PI - 90}) translate(${getRadius(d)},0)`
      )
      .attr('fill', d => getNodeColor(d.data))
      .attr('r', d => getNodeSize(d.depth, 'normal'))
      .attr('stroke', '#fff')
      .attr('stroke-width', 1.2)
      .on('mouseover', function (event, d) {
        RecoveryPrevNodeSize()
        const curRadius = getNodeSize(d.depth, 'hover')
        d3.select(`#node-${d.data.id}`).attr('r', curRadius)
        const tooltipsColor = getNodeColor(d.data)
        tooltip.transition().duration(200).style('opacity', 1)
        tooltip
          .html(options?.tooltip({ ...d.data, tooltipsColor }) || d.data.name)
          .style('border-color', tooltipsColor)
          .style('color', tooltipsColor)
          .style('left', event.pageX + 25 + 'px')
          .style('top', event.pageY - 28 + 'px')
        // 降低其他节点的透明度
        nodes.transition().duration(100).attr('opacity', 0.1)
        links.transition().duration(100).attr('opacity', 0.1)
        // 高亮当前节点及其子节点
        highlightNode(d.data)
      })
      .on('mouseout', function (event, d) {
        const originRadius = getNodeSize(d.depth, 'normal')
        d3.select(`#node-${d.data.id}`).attr('r', originRadius)
        tooltip.transition().duration(500).style('opacity', 0)
        // 恢复所有节点的透明度
        nodes.transition().duration(100).attr('opacity', 1)
        links.transition().duration(100).attr('opacity', 1)
      })
      .on('click', function (event, d) {
        options?.clickNode?.(d, event)
      })

    const tooltip = d3
      .select(Dom.value)
      .append('div')
      .style('border-radius', '4px')
      .style('background-color', '#fff')
      .style('padding', '5px 10px')
      .style('border-style', 'solid')
      .style('border-width', '2px')
      .style('position', 'absolute')

    NODES.value = nodes
    LINKS.value = links
    TOOLTIP.value = tooltip

    d3.select(`#d3-graph-svg`)
      .transition()
      .duration(700)
      .attr('transform', `scale(1)`)
  }
  function highlightNode(node, parentId) {
    d3.select(`#node-${node.id}`).transition().duration(100).attr('opacity', 1)
    if (parentId !== undefined) {
      d3.select(`#link-${parentId}-${node.id}`)
        .transition()
        .duration(100)
        .attr('opacity', 1)
    }
    if (node.children) {
      node.children.forEach(child => highlightNode(child, node.id))
    }
  }
  const findNodeById = (nodeId, data = chartsData.value) => {
    // 找到当前节点
    if (data) {
      const children = data.children || []
      if (data.id === nodeId) {
        return data
      } else if (children && children.length > 0) {
        for (let i = 0; i < children.length; i++) {
          const res = findNodeById(nodeId, children[i])
          if (res) {
            return res
          }
        }
      }
    }
  }
  const highlightNodeById = nodeId => {
    // 找到当前节点
    const nodex = document.getElementById(`node-${nodeId}`)
    const curr = findNodeById(nodeId)
    console.log(nodex, curr, 'nodex')
    if (curr && nodex) {
      const tooltipsColor = getNodeColor(curr)
      const bbox = nodex.getBoundingClientRect()
      const x = bbox.x + window.scrollX
      const y = bbox.y + window.scrollY
      TOOLTIP.value.transition().duration(200).style('opacity', 0.9)
      TOOLTIP.value
        .html(options?.tooltip({ ...curr, tooltipsColor }) || curr.name)
        .style('border-color', tooltipsColor)
        .style('left', x + 25 + 'px')
        .style('top', y - 28 + 'px')
      // 降低其他节点的透明度
      NODES.value && NODES.value.attr('opacity', 0.1)
      LINKS.value && LINKS.value.attr('opacity', 0.1)
      // 若有放大的节点，恢复其大小
      RecoveryPrevNodeSize()
      // 高亮当前节点及其子节点
      const d = d3.select(`#node-${nodeId}`).datum()
      // 记录放大的节点
      prevNode.value = d
      console.log(d, 'd')
      const curRadius = getNodeSize(d.depth, 'hover')
      d3.select(`#node-${d.data.id}`).attr('r', curRadius)
      highlightNode(curr)
      return curr
    }
  }
  const RecoveryPrevNodeSize = () => {
    if (prevNode.value) {
      const curRadius = getNodeSize(prevNode.value.depth, 'normal')
      d3.select(`#node-${prevNode.value.data.id}`).attr('r', curRadius)
      prevNode.value = null
    }
  }
  const resizeObserver = new ResizeObserver(() => {
    useDebounceFn(createChart, 300)()
  })

  watch(
    () => Dom,
    () => {
      Dom.value && resizeObserver.observe(Dom.value)
    },
    { deep: true }
  )

  tryOnUnmounted(() => {
    resizeObserver.disconnect()
  })

  return {
    createChart,
    highlightNodeById
  }
}
